package sim.model;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import sim.utils.GenVarAle.VariableAleatoria;

/**
 * Clase que permite configurara algunos parámetros del modelo y calcula la
 * demora de la reparación.
 * 
 * @author alejandro
 * 
 */
public class Config {

	/**
	 * Los nombres de las columnas de la tabla.
	 */
	private static final String[] columnNames = { "<html>Días<br>(Clock)",
			"<html>Evento", "<html>RND avión <br>roto",
			"<html>Próx avión <br>roto", "<html>Próx avión <br>control",
			"<html>Cant aviones <br>en raparac",
			"<html>Cant aviones <br>disponibles",
			"<html>Macanicos <br>reparando", "<html>RND <br>reparacion",
			"<html>Tiempo<br>reparacion", "<html>Próx fin <br>reparación",
			"<html>Max cant <br>mec trab", "<html>Cant Av <br>rep" };

	/**
	 * Cantidad de columnas del tipo "prox fin rep Av N" se van a mostrar en la
	 * tabla. Default = 15
	 */
	private static final int CANT_COLUMNAS_FINREPAV = 20;

	/**
	 * Lleva la cuenta de la cantidad de aviones simulados hasta el momento.
	 */
	private int contAvionesSimulados = 0;

	/* La simulación termina cuando ocurra alguno de los dos siguientes. */
	/**
	 * Cantidad de días a simular. Terminar la simulación cuando se llegue a
	 * esta cantidad de días. Default = 100
	 */
	private double SIMULACION_DIAS = 100;

	/**
	 * Cantidad de aviones a simular. Es la canidad de aviones a reparar antes
	 * de terminal la simulación. Default = 1000
	 */
	private int SIMULACION_AVIONES = 1000;

	/**
	 * Intervalo de rotura de aviones en días. Hay 200 aviones. Los aviones se
	 * descomponen dos veces por año 200*2=400 roturas por año. 365/400=0,9125.
	 * Cada 0,9125 dias se rompe un avión. Default = 0.9125
	 */
	// private double INTERVALO_ROTURA_AVION = 0.9125;
	private double INTERVALO_ROTURA_AVION = 1.095890411;

	/**
	 * Intervalo de control de aviones en días. Hay 200 aviones. Los aviones se
	 * controlan cada 5 meses 5*31=155 Cada avión se controla cada 155 dias.
	 * 155/200=0,775. Cada 0,755 dias se controla un avion. Default = 0.775
	 */
	private double INTERVALO_CONTROL_AVION = 0.775;

	/**
	 * Cantidad de mecanicos necesarios por cada avion que se repara o controla.
	 * Default = 5
	 */
	private int CANT_MECANICOS_PARA_REPARACION = 5;

	/**
	 * Define si la reparacion demora un tiempo variable que responde a una
	 * disptribución negativa (true) o es fija (false). Default = true
	 */
	private static final boolean REPARACION_EXP_NEGATIVA = false;
	// private static final boolean REPARACION_EXP_NEGATIVA = false;

	/**
	 * La media usada para el calculo de la reparación cuando
	 * REPARACION_EXP_NEGATIVA=true. Distribución Exponencial Negativa. En el
	 * ejercicio es una semana (o sea 7 días). 1/7=0.14285714285714285. La
	 * formula queda -(1/(1/7))*ln(1-rnd) = -(7)*ln(1-rnd) Default = 1/7
	 */
	/*** DEFAULT ***/
	// private static final double MEDIA_REPARACION = 0.14285714285714285;
	/*** DEFAULT ***/
	// private static final double MEDIA_REPARACION = (double) 1 / 7;

	// private static final double MEDIA_REPARACION = (double) 1 / 5;
	private double MEDIA_REPARACION = (double) 1 / 5;

	/**
	 * Configura la cantidad de dias que demora la reparación cuando tiene
	 * demora fija, es decir, cuando REPARACION_EXP_NEGATIVA=false. Default = 7
	 */
	private static final double FIJO_REPARACION = 7;

	// -------------------------------------------------------------------------
	/**
	 * Cuando decimos "¿cuántos mecánicos se necesitan para asegurar que hay por
	 * lo menos 95% de probabilidad de que 185 o más aviones estén
	 * dispondibles?" las siguientes dos variables permiten especificar la
	 * cantidad de aviones mínima que se desean en servicio y la probabilidad de
	 * que exista esa cantidad de aviones en servicio en un momento dado. Es
	 * decir, "hay por lo menos PROB_AVIONES_MINIMA% de probabilidad de que
	 * CANT_AVIONES_MINIMA o más aviones estén dispondibles"
	 */
	/**
	 * La cantidad mínima de aviones que debe haber en servicio en un momento
	 * dado. Default = 185
	 */
	private static final int CANT_AVIONES_MINIMA = 185;

	/**
	 * La probabilidad de que existan CANT_AVIONES_MINIMA en un momento dado.
	 * Porcentaje de tiempo en la que tiene que haber al menos
	 * CANT_AVIONES_MINIMA en servicio. Default = 95
	 */
	private static final int PROB_AVIONES_MINIMA = 95;
	// -------------------------------------------------------------------------

	/**
	 * Define si se ordenan los elementos de la lista de porximas finalizaciones
	 * de las reparaciones de los aviones de forma ascendente de manera que se
	 * puedan ver los próximos aviones que van a finalizar. Default = true
	 */
	private static final boolean SORT_FINREPAV_ELEMENTS = false;
	// private static final boolean SORT_FINREPAV_ELEMENTS = false;

	/**
	 * Determina si los aviones que se terminaron de reparar se dejan en el
	 * vector <i>proxFinRepAv</i> de próximas finalizaciones de reparaciones de
	 * aviones o si se eliminan. true se dejan, false se eliminan. Default =
	 * false.
	 */
	private static final boolean LEAVE_FINISHED_PLANES = false;
	// private static final boolean LEAVE_FINISHED_PLANES = true;

	/**
	 * Atributo que contine el generador de números aleatorios.
	 */
	private VariableAleatoria generador;
	private VariableAleatoria generadorRoturas;

	// ========================================================================
	// COMIENZO DE MÉTODOS.

	/**
	 * Constructor por defecto.
	 */
	public Config() {
		if (Config.REPARACION_EXP_NEGATIVA) {
			this.generador = new VariableAleatoria(
					VariableAleatoria.DISTRIBUCION_EXP_NEGATIVA);
			generador.setExpNeg(this.MEDIA_REPARACION);
		}
		generadorRoturas = new VariableAleatoria(
				VariableAleatoria.DISTRIBUCION_EXP_NEGATIVA);
		generadorRoturas.setExpNeg(this.INTERVALO_ROTURA_AVION);
	}

	/**
	 * Devuelve el intervalo de rotura de aviones en días.
	 * 
	 * @return El intervalo de rotura de aviones en días.
	 */
	public double getIntervaloRoturaAvion() {
		// return INTERVALO_ROTURA_AVION;
		return generadorRoturas.next();
	}

	/**
	 * Configura el intervalo de rotura de aviones en días.
	 * 
	 * @param iNTERVALO_ROTURA_AVION
	 *            the intervalo to set
	 */
	public void setIntervaloRoturaAvion(double intervalo) {
		INTERVALO_ROTURA_AVION = intervalo;
	}

	/**
	 * Devuelve el intervalo de control de aviones en días.
	 * 
	 * @return El intervalo de control de aviones en días.
	 */
	public double getIntervaloControlAvion() {
		return INTERVALO_CONTROL_AVION;
	}

	/**
	 * Configura el intervalo de control de aviones en días.
	 * 
	 * @param intervalo
	 *            the intervalo to set
	 */
	public void setIntervaloControlAvion(double intervalo) {
		INTERVALO_CONTROL_AVION = intervalo;
	}

	/**
	 * Devuelve la cantidad de mecanicos necesarios por cada avion que se repara
	 * o controla.
	 * 
	 * @return La cantidad de mecanicos necesarios por cada avion que se repara
	 *         o controla.
	 */
	public int getCantMecanicosParaReparacion() {
		return CANT_MECANICOS_PARA_REPARACION;
	}

	/**
	 * Configura la cantidad de mecanicos necesarios por cada avion que se
	 * repara o controla.
	 * 
	 * @param cantidad
	 *            La cantidad de mecanicos necesarios por cada avion que se
	 *            repara o controla.
	 */
	public void setCantMecanicosParaReparacion(int cantidad) {
		this.CANT_MECANICOS_PARA_REPARACION = cantidad;
	}

	/**
	 * Devuelve la media usada para el calculo de la reparación cuando
	 * REPARACION_EXP_NEGATIVA=true.
	 * 
	 * @return La media usada para el calculo de la reparación.
	 */
	public double getMediaReparacion() {
		return MEDIA_REPARACION;
	}

	/**
	 * Configura la media usada para el calculo de la reparación cuando
	 * REPARACION_EXP_NEGATIVA=true.
	 * 
	 * @param lambda
	 *            La media usada para el calculo de la variable exponencial
	 *            negativa de la reparación (valor Lambda de la fórmula).
	 */
	public void setMediaReparación(double lambda) {
		this.MEDIA_REPARACION = lambda;
	}

	/**
	 * Devuelve la demora en la reparación. Puede calcularla usando una función
	 * exponencial negativa con la media configurada o un valor fijo, depende de
	 * como se haya configurado.
	 * 
	 * @return La demora en la reparación.
	 */
	public double getReparacion() {
		if (Config.REPARACION_EXP_NEGATIVA) {
			return generador.next();
		} else {
			return Config.FIJO_REPARACION;
		}
	}

	/**
	 * Configura los parámetros de final de la simulación. La simulación va a
	 * terminar cuando alguno de las dos condiciones se cumpla.
	 * 
	 * @param simulacionDias
	 *            Cantidad de días a simular.
	 * @param simulacionAviones
	 *            Cantidad de Aviones a simular.
	 */
	public void setSimulationEnd(double simulacionDias, int simulacionAviones) {
		this.SIMULACION_DIAS = simulacionDias;
		this.SIMULACION_AVIONES = simulacionAviones;
	}

	/**
	 * Calcula si la simulación se terminó o no dependiendo de la cantidad de
	 * días y de aviones que se hayan configurado para simular y comparandolo
	 * con la cantidad de días y aviones simulados hasta el momento, valores que
	 * deben ser pasados por parámetros.
	 * 
	 * @param dias
	 *            Los días actuales de la simulación.
	 * @return true si la simulación termino - false si la simulación debe
	 *         continuar.
	 */
	public boolean simulationHasEnded(double dias) {
		return (dias >= this.SIMULACION_DIAS || this.contAvionesSimulados >= this.SIMULACION_AVIONES);
	}

	/**
	 * Devuelve la cantidad de días a simular.
	 * 
	 * @return La cantidad de días a simular.
	 */
	public double getSimulacionDias() {
		return SIMULACION_DIAS;
	}

	/**
	 * Devuelve la cantidad de Aviones a simular.
	 * 
	 * @return La cantidad de Aviones a simular.
	 */
	public int getSimulacionAviones() {
		return SIMULACION_AVIONES;
	}

	/**
	 * Devuelve la cantidad mínima de aviones que se espera que haya en servicio
	 * en un momento dado.
	 * 
	 * @return La cantidad mínima de aviones que se espera que haya en servicio
	 *         en un momento dado.
	 */
	public static int getCantAvionesMinima() {
		return CANT_AVIONES_MINIMA;
	}

	/**
	 * La probabilidad de que existan getCantAvionesMinima() en un momento dado.
	 * Porcentaje de tiempo en la que tiene que haber al menos
	 * getCantAvionesMinima() en servicio.
	 * 
	 * @return La probabilidad de que existan en servicio al menos la cantidad
	 *         mínima de aviones.
	 */
	public static int getProbAvionesMinima() {
		return PROB_AVIONES_MINIMA;
	}

	/**
	 * Define si se ordenan los elementos de la lista de porximas finalizaciones
	 * de las reparaciones de los aviones de forma ascendente de manera que se
	 * puedan ver los próximos aviones que van a finalizar.
	 * 
	 * @return true si se ordenan lo elementos - false si dejan en el orden en
	 *         que se cargaron.
	 */
	public static boolean isSortFinrepavElements() {
		return SORT_FINREPAV_ELEMENTS;
	}

	/**
	 * 
	 * Determina si los aviones que se terminaron de reparar se dejan en el
	 * vector <i>proxFinRepAv</i> de próximas finalizaciones de reparaciones de
	 * aviones o si se eliminan.
	 * 
	 * @return true se dejan, false se eliminan.
	 */
	public static boolean isLeaveFinishedPlanes() {
		return LEAVE_FINISHED_PLANES;
	}

	/**
	 * Suma uno al contador de aviones simulados (simulados, no reparados).
	 * Sirve para calcular el final de la simulacion.
	 * 
	 * @return Devuelve el valor actual del contador luego de sumar.
	 */
	public int addAvionSimulado() {
		this.contAvionesSimulados++;
		return this.contAvionesSimulados;
	}

	/**
	 * Método que devuelve el número random usado para generar el último valor
	 * de la variable aleatoria.
	 * 
	 * @return El último random usado o -1 si no se generó ninguno.
	 */
	public double getLastRandom() {
		return Config.REPARACION_EXP_NEGATIVA ? this.generador.getLastRandom()
				: 0;
	}

	/**
	 * Método que devuelve el número random usado para generar el proximo avion
	 * a reparar.
	 * 
	 * @return El último random usado.
	 */
	public double getLastRandomRoturas() {
		return this.generadorRoturas.getLastRandom();
	}

	/**
	 * Devuelve un vector de Strings con los nombres de las columnas de la tabla
	 * (JTable) de la ventana (JFrame).
	 * 
	 * @return Un vector con los nombres de las columnas de la tabla.
	 */
	public static String[] getColumnNames() {
		List<String> columnNames = new ArrayList<String>(
				Arrays.asList(Config.columnNames));

		int i;
		for (i = 1; i <= Config.CANT_COLUMNAS_FINREPAV; i++) {
			columnNames.add("<html>prox fin <br>rep Av " + i);
		}

		return columnNames.toArray(new String[columnNames.size()]);
	}

	/**
	 * Devuelve la cantidad de columnas que se usan para mostrar la proxima
	 * finalización de reparaciones de aviones.
	 * 
	 * @return the cantColumnasFinrepav
	 */
	public static int getCantColumnasFinrepav() {
		return CANT_COLUMNAS_FINREPAV;
	}

}
